package com.cragon.proxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Locale;

/**
 * @Description: 动态代理实现类
 * @Author: ZHOUHAILONG
 * @Date: 2021年03月03日 19:36
 */
public class DynamicProxy {
	public static Object getInstance(Object target) {
		Class clazz = target.getClass().getInterfaces()[0];
		// 最终类内容
		String content = "";
		// 换行
		String line = "\n";
		// 缩进
		String tab = "\t";
		// 目标接口的名称
		String targetInterfaceName = clazz.getSimpleName();
		// 包路径
		String packageContent = "package com.cragon.proxy;" + line;
		// 导入的目标类
		String importContent = "import " + clazz.getName() + ";" + line + line;
		// 代理类首行代码
		String clazzFirstLineContent = "public class $ProxyCragon implements " + targetInterfaceName + " {" + line;
		// 定义代理类中的属性
		String fieldContent = tab + "private " + targetInterfaceName + " target;" + line;
		String constructorContent = tab + "public $ProxyCragon(" + targetInterfaceName + " target) {" + line +
				tab + tab + "this.target = target;" + line +
				tab + "}" + line;
		// 方法内容
		String methodContent = "";
		// 遍历目标接口的所有目标方法
		Method[] targetMethods = clazz.getDeclaredMethods();
		for (Method method : targetMethods) {
			// 方法返回类型的名称
			String returnTypeName = method.getReturnType().getSimpleName();
			// 方法的名称
			String methodName = method.getName();
			// 方法参数的类型
			Class<?>[] args = method.getParameterTypes();
			// 代理方法中定义接收的参数内容
			String proxyArgsContent = "";
			// 目标方法要使用的参数内容
			String targetArgsContent = "";
			// 区分代理对象方法参数名称的后缀
			int flag = 0;
			for (Class<?> arg : args) {
				// 参数类型名称
				String temp = arg.getSimpleName();
				proxyArgsContent += temp + " p" + flag + ", ";
				targetArgsContent += "p" + flag + ", ";
				flag++;
			}

			// 去除最后无效的逗号
			if (proxyArgsContent.length() > 0) {
				proxyArgsContent = proxyArgsContent.substring(0, proxyArgsContent.lastIndexOf(","));
				targetArgsContent = targetArgsContent.substring(0, targetArgsContent.lastIndexOf(","));
			}

			// 拼接代理方法的内容
			methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + proxyArgsContent + ") {" + line +
					tab + tab + "System.out.println(\"log\");" + line +
					tab + tab + (!returnTypeName.equals("void") ? "return " : "") + "target." + methodName + "(" + targetArgsContent + ");" + line +
					tab + "}" + line;
		}

		content += packageContent + importContent + clazzFirstLineContent + fieldContent + constructorContent + methodContent + "}";

		// 生成代理文件
		String absolutePath = new File("").getAbsolutePath();
		File file = new File(absolutePath + "\\study\\src\\main\\java\\com\\cragon\\proxy\\$ProxyCragon.java");
		try {
			if (!file.exists()) {
				file.createNewFile();
			}

			FileWriter fw = new FileWriter(file);
			fw.write(content);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 获取Java编译器
		/*JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
		Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(file);

		JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
		task.call();*/
		try {
//			fileManager.close();

			URL[] urls = new URL[]{new URL("file:E:\\\\")};
			URLClassLoader urlClassLoader = new URLClassLoader(urls);
			Class proxy = urlClassLoader.loadClass("com.cragon.proxy.$ProxyCragon");
			Constructor constructor = proxy.getConstructor(clazz);
			Object o = constructor.newInstance(target);

			return o;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
}

// package com.cragon;
// import com.cragon.Target;
// public class $ProxyCragon implements Target {
// 		private Target target;
// 		public $ProxyCragon(Target target) {
//			this.target = target;
//		}
